import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'dart:async';

void main() async {
  print('🚀 隧道客户端程序');

  // 检查目标服务是否运行
  if (!await checkTargetService('127.0.0.1:3000')) {
    print('❌ 目标服务未运行在端口3000，请先启动您的服务');
    exit(1);
  }
  print('✅ 检测到目标服务运行在端口3000');

  // 创建隧道
  TunnelData? tunnel;
  try {
    tunnel = await createTunnel();
  } catch (e) {
    print('创建隧道失败: $e');
    exit(1);
  }

  // 连接到隧道服务器
  try {
    await connectToTunnel(tunnel);
  } catch (e) {
    print('连接隧道失败: $e');
    exit(1);
  }

  print('🎉 隧道连接成功，开始转发请求...');
  print('💡 外部可通过以下地址访问您的服务:');
  print('   🌐 http://127.0.0.1:${tunnel.publicPort}/');
  print('   📋 所有请求将转发到: http://127.0.0.1:3000/');
  print('');
  print('💡 测试命令:');
  print('   curl http://127.0.0.1:${tunnel.publicPort}/');
  print(
    '   curl -H \'Content-Type: application/json\' http://127.0.0.1:${tunnel.publicPort}/api/test',
  );
  print('');
  print('💡 按 Ctrl+C 退出程序');

  // 保持程序运行
  await Completer<void>().future;
}

// 检查目标服务是否可用
Future<bool> checkTargetService(String address) async {
  try {
    final parts = address.split(':');
    final host = parts[0];
    final port = int.parse(parts[1]);

    // 尝试TCP连接
    Socket? socket;
    try {
      socket = await Socket.connect(host, port, timeout: Duration(seconds: 2));
      await socket.close();
    } catch (e) {
      print('💡 提示: 请确保您的服务运行在端口3000');
      print('💡 例如: python3 -m http.server 3000');
      return false;
    }

    // 发送HTTP请求验证服务可用性
    final client = HttpClient();
    client.connectionTimeout = Duration(seconds: 2);

    try {
      final request = await client.get(host, port, '/');
      final response = await request.close();
      await response.drain();
      return true;
    } catch (e) {
      print('💡 端口3000有服务但无法响应HTTP请求');
      return false;
    } finally {
      client.close();
    }
  } catch (e) {
    return false;
  }
}

class TunnelData {
  final String tunnelId;
  final String tunnelHost;
  final String tunnelPort;
  final String publicPort;

  TunnelData({
    required this.tunnelId,
    required this.tunnelHost,
    required this.tunnelPort,
    required this.publicPort,
  });

  factory TunnelData.fromJson(Map<String, dynamic> json) {
    return TunnelData(
      tunnelId: json['tunnelId'] as String,
      tunnelHost: json['tunnelHost'] as String,
      tunnelPort: json['tunnelPort'] as String,
      publicPort: json['publicPort'] as String,
    );
  }
}

Future<TunnelData> createTunnel() async {
  print('🔧 创建隧道映射...');

  final requestData = {
    'serverName': 'local-service-3000',
    'localHost': '127.0.0.1',
    'localPort': '3000',
    'tunnelType': 'http',
  };

  final client = HttpClient();
  try {
    final request = await client.post('localhost', 8080, '/api/create/tunnel');
    request.headers.contentType = ContentType.json;

    final jsonData = utf8.encode(jsonEncode(requestData));
    request.add(jsonData);

    final response = await request.close();
    final responseBody = await response.transform(utf8.decoder).join();

    print('📄 创建隧道响应: $responseBody');

    final responseJson = jsonDecode(responseBody) as Map<String, dynamic>;
    final tunnelData = TunnelData.fromJson(
      responseJson['data'] as Map<String, dynamic>,
    );

    print('✅ 隧道创建成功:');
    print('   隧道ID: ${tunnelData.tunnelId}');
    print('   公网端口: ${tunnelData.publicPort}');
    print('   隧道端口: ${tunnelData.tunnelPort}');

    return tunnelData;
  } finally {
    client.close();
  }
}

Future<void> connectToTunnel(TunnelData tunnel) async {
  print('🔗 连接到隧道服务器端口 9001...');

  // 连接到隧道服务器的TCP端口 (固定9001)
  final socket = await Socket.connect('127.0.0.1', 9001);
  print('✅ TCP连接已建立');

  // 直接发送隧道ID字符串，不是JSON
  print('📡 发送隧道ID: ${tunnel.tunnelId}');

  socket.add(utf8.encode(tunnel.tunnelId));
  print('📡 隧道ID已发送，等待认证确认...');

  // 等待认证确认
  await Future.delayed(Duration(seconds: 2));

  // 启动消息处理
  handleConnection(socket, tunnel);
}

void handleConnection(Socket socket, TunnelData tunnel) {
  print('🔄 隧道已就绪，等待HTTP请求转发...');

  socket.listen(
    (Uint8List data) {
      final msg = utf8.decode(data);
      print('📨 收到隧道服务器消息 (${data.length} bytes): $msg');

      // 处理HTTP请求转发
      if (data.contains(utf8.encode('GET')[0]) ||
          data.contains(utf8.encode('POST')[0])) {
        print('🌐 检测到HTTP请求，开始转发...');
        handleHTTPRequest(socket, data);
      }
    },
    onError: (error) {
      print('❌ 连接读取失败: $error');
    },
    onDone: () {
      print('🔌 隧道连接已关闭 (隧道ID: ${tunnel.tunnelId})');
      socket.close();
    },
  );
}

class RequestPacket {
  final String type;
  final String requestId;
  final String method;
  final String path;
  final Map<String, List<String>> headers;
  final Uint8List body;

  RequestPacket({
    required this.type,
    required this.requestId,
    required this.method,
    required this.path,
    required this.headers,
    required this.body,
  });

  factory RequestPacket.fromJson(Map<String, dynamic> json) {
    final headers = <String, List<String>>{};
    final headersJson = json['headers'] as Map<String, dynamic>?;
    if (headersJson != null) {
      headersJson.forEach((key, value) {
        if (value is List) {
          headers[key] = value.cast<String>();
        } else {
          headers[key] = [value.toString()];
        }
      });
    }

    Uint8List body = Uint8List(0);
    if (json['body'] != null) {
      if (json['body'] is String) {
        body = Uint8List.fromList(utf8.encode(json['body'] as String));
      } else if (json['body'] is List) {
        body = Uint8List.fromList((json['body'] as List).cast<int>());
      }
    }

    return RequestPacket(
      type: json['type'] as String? ?? '',
      requestId: json['requestId'] as String? ?? '',
      method: json['method'] as String? ?? '',
      path: json['path'] as String? ?? '',
      headers: headers,
      body: body,
    );
  }
}

void handleHTTPRequest(Socket socket, Uint8List httpData) async {
  print('🔄 处理HTTP请求转发...');

  try {
    // 解析收到的JSON请求
    final jsonString = utf8.decode(httpData);
    final requestJson = jsonDecode(jsonString) as Map<String, dynamic>;
    final requestPacket = RequestPacket.fromJson(requestJson);

    final requestIdShort = requestPacket.requestId.length > 8
        ? requestPacket.requestId.substring(0, 8)
        : requestPacket.requestId;
    print(
      '📋 收到HTTP请求: ${requestPacket.method} ${requestPacket.path} (ID: $requestIdShort)',
    );

    // 连接到本地服务
    Socket localSocket;
    try {
      localSocket = await Socket.connect('127.0.0.1', 3000);
    } catch (e) {
      print('❌ 连接本地服务失败: $e');
      sendErrorResponse(socket, requestPacket.requestId, '连接本地服务失败: $e');
      return;
    }

    try {
      // 重构HTTP请求
      final httpRequest = reconstructHTTPRequest(requestPacket);
      print('📤 转发到本地服务: ${requestPacket.method} ${requestPacket.path}');

      // 发送HTTP请求到本地服务器
      localSocket.add(utf8.encode(httpRequest));

      // 读取本地服务器的完整HTTP响应
      final responseBuffer = <int>[];
      final completer = Completer<void>();
      Timer? timeoutTimer;

      // 设置超时
      timeoutTimer = Timer(Duration(seconds: 10), () {
        if (!completer.isCompleted) {
          print('❌ 读取本地响应超时');
          sendErrorResponse(socket, requestPacket.requestId, '读取响应超时');
          completer.complete();
        }
      });

      localSocket.listen(
        (Uint8List data) {
          responseBuffer.addAll(data);

          // 检查是否读取完整响应
          if (_isCompleteHTTPResponse(Uint8List.fromList(responseBuffer))) {
            timeoutTimer?.cancel();
            if (!completer.isCompleted) {
              completer.complete();
            }
          }

          // 防止无限增长 - 1MB限制
          if (responseBuffer.length > 1024 * 1024) {
            timeoutTimer?.cancel();
            if (!completer.isCompleted) {
              completer.complete();
            }
          }
        },
        onError: (error) {
          timeoutTimer?.cancel();
          if (!completer.isCompleted) {
            print('❌ 读取本地响应失败: $error');
            sendErrorResponse(
              socket,
              requestPacket.requestId,
              '读取响应失败: $error',
            );
            completer.complete();
          }
        },
        onDone: () {
          timeoutTimer?.cancel();
          if (!completer.isCompleted) {
            completer.complete();
          }
        },
      );

      await completer.future;
      await localSocket.close();

      if (responseBuffer.isEmpty) {
        sendErrorResponse(socket, requestPacket.requestId, '服务器返回空响应');
        return;
      }

      print('📥 本地服务响应 (${responseBuffer.length} bytes)');

      // 解析HTTP响应
      final responseData = Uint8List.fromList(responseBuffer);
      final parsedResponse = parseHTTPResponse(responseData);
      final statusCode = parsedResponse['statusCode'] as int;
      final headers = parsedResponse['headers'] as Map<String, List<String>>;
      final body = parsedResponse['body'] as Uint8List;

      // 构建JSON响应包
      final responsePacket = {
        'type': 'response',
        'requestId': requestPacket.requestId,
        'status': statusCode,
        'headers': headers,
        'body': body.toList(), // 转换为List<int>以便JSON序列化
      };

      // 发送JSON响应回隧道服务器
      final jsonResponse = jsonEncode(responsePacket);
      socket.add(utf8.encode(jsonResponse));

      print(
        '✅ 请求处理完成: ${requestPacket.method} ${requestPacket.path} → $statusCode',
      );
    } catch (e) {
      await localSocket.close();
      print('❌ 处理请求时发生错误: $e');
      sendErrorResponse(socket, requestPacket.requestId, '处理请求失败: $e');
    }
  } catch (e) {
    print('❌ 解析HTTP请求JSON失败: $e');
  }
}

// 检查是否为完整的HTTP响应
bool _isCompleteHTTPResponse(Uint8List responseBuffer) {
  // 查找头部结束标记
  final headerEndPattern = utf8.encode('\r\n\r\n');
  final headerEndIndex = _indexOf(responseBuffer, headerEndPattern);

  if (headerEndIndex == -1) {
    // 尝试 \n\n 分隔符
    final altPattern = utf8.encode('\n\n');
    final altIndex = _indexOf(responseBuffer, altPattern);
    if (altIndex == -1) {
      return false; // 还没有完整的头部
    }
  }

  final headerEnd = headerEndIndex != -1
      ? headerEndIndex
      : _indexOf(responseBuffer, utf8.encode('\n\n'));
  final headers = utf8.decode(responseBuffer.sublist(0, headerEnd));

  // 检查Content-Length
  if (headers.contains('Content-Length:')) {
    final lines = headers.split('\n');
    for (final line in lines) {
      final trimmedLine = line.trim();
      if (trimmedLine.startsWith('Content-Length:')) {
        final lengthStr = trimmedLine.split(':')[1].trim();
        final contentLength = int.tryParse(lengthStr);
        if (contentLength != null) {
          final bodyStart = headerEnd + (headerEndIndex != -1 ? 4 : 2);
          return responseBuffer.length >= bodyStart + contentLength;
        }
        break;
      }
    }
  }

  // 检查chunked编码
  if (headers.toLowerCase().contains('transfer-encoding: chunked')) {
    final endPattern = utf8.encode('\r\n0\r\n\r\n');
    return _indexOf(responseBuffer, endPattern) != -1;
  }

  // 对于其他情况，等待更多数据或超时
  return false;
}

// 在字节数组中查找模式的索引
int _indexOf(Uint8List source, Uint8List pattern) {
  if (pattern.isEmpty || source.length < pattern.length) {
    return -1;
  }

  for (int i = 0; i <= source.length - pattern.length; i++) {
    bool found = true;
    for (int j = 0; j < pattern.length; j++) {
      if (source[i + j] != pattern[j]) {
        found = false;
        break;
      }
    }
    if (found) {
      return i;
    }
  }
  return -1;
}

// 重构HTTP请求
String reconstructHTTPRequest(RequestPacket packet) {
  var request = '${packet.method} ${packet.path} HTTP/1.1\r\n';

  // 添加头部
  packet.headers.forEach((key, values) {
    for (final value in values) {
      request += '$key: $value\r\n';
    }
  });

  // 确保有Host头部
  if (!packet.headers.containsKey('Host')) {
    request += 'Host: localhost:3000\r\n';
  }

  // 添加Content-Length如果有body
  if (packet.body.isNotEmpty) {
    request += 'Content-Length: ${packet.body.length}\r\n';
  }

  request += '\r\n';

  // 添加body
  if (packet.body.isNotEmpty) {
    request += utf8.decode(packet.body);
  }

  return request;
}

// 解析HTTP响应，保持原始数据完整性
Map<String, dynamic> parseHTTPResponse(Uint8List responseData) {
  if (responseData.isEmpty) {
    return {
      'statusCode': 500,
      'headers': <String, List<String>>{},
      'body': Uint8List.fromList(utf8.encode('Empty response')),
    };
  }

  // 查找头部和body的分隔符
  var separator = utf8.encode('\r\n\r\n');
  var sepIndex = _indexOf(responseData, separator);

  if (sepIndex == -1) {
    // 尝试\n\n分隔符
    separator = utf8.encode('\n\n');
    sepIndex = _indexOf(responseData, separator);
    if (sepIndex == -1) {
      // 没有找到分隔符，可能是纯文本响应
      return {
        'statusCode': 200,
        'headers': <String, List<String>>{
          'Content-Type': ['text/plain'],
        },
        'body': responseData,
      };
    }
  }

  final headerBytes = responseData.sublist(0, sepIndex);
  final body = responseData.sublist(sepIndex + separator.length);

  final headerStr = utf8.decode(headerBytes);
  final lines = headerStr.split('\n');

  // 解析状态行
  var statusCode = 200;
  if (lines.isNotEmpty) {
    final statusLine = lines[0].trim();
    if (statusLine.startsWith('HTTP/')) {
      final parts = statusLine.split(' ');
      if (parts.length >= 2) {
        final code = int.tryParse(parts[1]);
        if (code != null) {
          statusCode = code;
        }
      }
    }
  }

  // 解析头部，保持原始格式
  final headers = <String, List<String>>{};
  for (int i = 1; i < lines.length; i++) {
    final line = lines[i].trim();
    if (line.isEmpty) {
      continue;
    }

    final colonIndex = line.indexOf(':');
    if (colonIndex > 0) {
      final key = line.substring(0, colonIndex).trim();
      final value = line.substring(colonIndex + 1).trim();

      // 如果头部已存在，追加值（支持多值头部）
      if (headers.containsKey(key)) {
        headers[key]!.add(value);
      } else {
        headers[key] = [value];
      }
    }
  }

  return {'statusCode': statusCode, 'headers': headers, 'body': body};
}

// 发送错误响应
void sendErrorResponse(Socket socket, String requestId, String errorMsg) {
  final responsePacket = {
    'type': 'response',
    'requestId': requestId,
    'status': 500,
    'headers': <String, List<String>>{
      'Content-Type': ['text/plain'],
    },
    'body': utf8.encode(errorMsg).toList(),
  };

  final jsonResponse = jsonEncode(responsePacket);
  socket.add(utf8.encode(jsonResponse));
}
